<?php
// +----------------------------------------------------------------------
// | INPHP
// | Copyright (c) 2023 https://inphp.cc All rights reserved.
// | Author: 幺月儿(https://gitee.com/lulanyin) Email: inphp@qq.com
// | 该文件的开源协议以所在项目的LICENSE文件为准，请遵守开源协议要求
// +----------------------------------------------------------------------
// | 收银台
// +----------------------------------------------------------------------
namespace app\finance\http\api;

use app\admin\attributes\auth;
use app\finance\model\CashierModel;
use app\finance\model\RechargeModel;
use app\finance\model\RefundModel;
use Inphp\Core\Db\PDO\Query;
use Inphp\Core\Middlewares;
use Inphp\Core\Modules;
use Inphp\Core\Object\Message;
use Inphp\Core\Util\Arr;
use Inphp\Core\Util\Str;

class cashier
{
    /**
     * UID
     * @var int
     */
    public int $uid = 0;

    /**
     * 账号
     * @var array
     */
    public array $user = [];

    /**
     * 获取收银单详情
     * @param array|null $params
     * @return Message
     */
    public function index(?array $params = null): Message
    {
        $params = $params ?? getClient()->get;
        $id = $params["id"] ?? 0;
        $id = is_numeric($id) && $id > 0 ? $id : 0;
        if ($id <= 0) {
            return httpMessage("ID参数无效");
        }
        $db = CashierModel::emptyQuery("c")
            ->where("id", $id)
            ->select([
                "c.*"
            ]);
        $admin = $params["admin"] ?? 0;
        $sso = Modules::getModule("sso");
        $this->user = authUserInfo();
        $admin = $admin == 1 && ($this->user["admin"] == 1 || ($sso && !empty($this->user["groups"]) && \app\sso\model\UserModel::matchPermission(["finance/cashier/list"], $this->user["groups"])));
        if ($admin) {
            $db->leftJoin(\app\sso\model\UserModel::tableName()." u", "c.uid=u.uid")
                ->addSelect([
                    "u.username, u.nickname"
                ]);
        }
        $item = $db->first();
        if (empty($item)) {
            return httpMessage("未找到收银单据");
        }
        if ($admin && $sso) {
            $item['nickname'] = \app\sso\model\UserModel::decodeNickname($item['nickname']);
        }
        $item["orderName"] = CashierModel::orderName($item["orderType"]);
        $item["currencyName"] = CashierModel::currencyName($item["currency"]);
        $item["currencyUnit"] = CashierModel::currencyUnit($item["currency"]);
        if (!$admin) {
            //非管理员，直接返回订单信息，用于支付，或简略查看
            $amount = bcsub($item["amount"], $item["payedAmount"], 2);
            return httpMessage([
                "id"            => $id,
                //订单号
                "orderId"       => $item["orderId"],
                //订单类型
                "orderType"     => $item["orderType"],
                //订单类型名称
                "orderName"     => $item["orderName"],
                //原订单需要支付的总金额
                "payAmount"     => $item["amount"],
                //已支付的金额
                "payedAmount"   => $item["payedAmount"],
                //剩余未支付的金额
                "amount"        => $amount,
                //结算货币代码
                "currency"      => $item["currency"],
                //结算货币名称
                "currencyName"  => $item["currencyName"],
                //结算货币单位
                "currencyUnit"  => $item["currencyUnit"],
                //是否已支付完成
                "payed"         => $item["amount"] > $amount ? 0 : 1,
                //订单拓展信息
                "info"          => $item["info"] ?? null
            ]);
        }
        $item["paymentName"] = CashierModel::getPayment($item["payment"]);
        //是否可以退款
        $item["canRefund"] = 0;
        //分销收款订单
        $item["sharing"] = 0;
        $item["sharingFinish"] = 0;
        if ($item["refund"] != 2 && $item["payedAmount"] > 0 && !empty($item["payedTime"]) && ($item["payedAmount"] - $item["refundAmount"] > 0) && ($item["sharing"] == 0 || ($item["sharing"] == 1 && $item["sharingFinish"] != 1))) {
            $date = strtotime($item["payedTime"]);
            $lastRefundTime = time() - 30 * 86400;
            if ($date > $lastRefundTime) {
                //仅允许当天支付的退款
                $item["canRefund"] = 1;
            }
        }
        //分账列表
        $item["sharingList"] = [];
        //优惠信息
        $promotionList = !empty($item["promotionList"]) ? json_decode($item["promotionList"], true) : [];
        $item["promotionList"] = [];
        foreach ($promotionList as $key => $promotion) {
            $item["promotionList"][] = [
                "id"            => $key,
                "name"          => $promotion["name"],
                "amount"        => bcdiv($promotion["amount"], 100, 2),
                "currency"      => $promotion["currency"]
            ];
        }
        return httpMessage($item);
    }

    /**
     * -- doc
     * name: 预览收银订单
     * desc: 通过传递订单类型、订单号进来，取得对应订单的收银付款数据
     * @return Message
     */
    public function prepay(): Message
    {
        //订单类型
        $orderType = REQUEST("orderType");
        $orderType = !empty($orderType) ? Str::trim($orderType) : null;
        if (empty($orderType)) {
            return httpMessage("缺少订单类型参数");
        }
        //订单号
        $orderId = REQUEST("orderId");
        $orderId = !empty($orderId) ? $orderId : 0;
        if ($orderId == 0) {
            return httpMessage("缺少订单号");
        }
        if ($orderType == "finance_recharge") {
            $order = RechargeModel::getRowByPrimaryKey($orderId);
            if (empty($order)) {
                return httpMessage("未找到订单数据");
            }
            $order["currency"] = "CNY";
            $order["payAmount"] = $order["amount"];
            $order["amount"] = bcsub($order["amount"], $order["payedAmount"], 2);
        } else {
            //以通知的方式取订单
            $outsideOrderTypes = CashierModel::outsideOrderTypes();
            if (!in_array($orderType, $outsideOrderTypes)) {
                return httpMessage("订单类型无效");
            }
            $order = Middlewares::process(FINANCE_GET_OUTSIDE_ORDER, [
                "orderType"     => $orderType,
                "orderId"       => $orderId
            ]);
            $order = is_array($order) ? mergeArray2to1($order) : [];
            if (empty($order)) {
                return httpMessage("未找到订单数据");
            }
        }
        //本次需要支付的金额
        $amount = $order["amount"] ?? 0;
        $amount = is_numeric($amount) && $amount > 0 ? $amount : 0;
        //原订单总金额
        $payAmount = $order["payAmount"] ?? 0;
        $payAmount = is_numeric($payAmount) && $payAmount > 0 ? $payAmount : 0;
        //已支付金额
        $payedAmount = $order["payedAmount"] ?? 0;
        $payedAmount = is_numeric($payedAmount) && $payedAmount > 0 ? $payedAmount : 0;
        //货币
        $currency = $order["currency"] ?? "CNY";
        $currencyName = $order["currencyName"] ?? CashierModel::currencyName($currency);
        $currencyUnit = $order["currencyUnit"] ?? CashierModel::currencyUnit($currency);
        //是否已完成支付
        $payed = $order["payed"] ?? ($amount > 0 ? 0 : 1);
        //订单名称
        $orderName = CashierModel::orderName($orderType);
        //拓展信息说明
        $info = $order["info"] ?? null;
        return httpMessage([
            //订单号
            "orderId"       => $orderId,
            //订单类型
            "orderType"     => $orderType,
            //订单类型名称
            "orderName"     => $orderName,
            //原订单需要支付的总金额
            "payAmount"     => $payAmount,
            //已支付的金额
            "payedAmount"   => $payedAmount,
            //剩余未支付的金额
            "amount"        => $amount,
            //结算货币代码
            "currency"      => $currency,
            //结算货币名称
            "currencyName"  => $currencyName,
            //结算货币单位
            "currencyUnit"  => $currencyUnit,
            //是否已支付完成
            "payed"         => $payed,
            //订单拓展信息
            "info"          => $info
        ]);
    }

    /**
     * -- doc
     * name: 生成收银单据
     * desc: 生成后，可用于提交支付
     * @return Message
     */
    public function generate(): Message
    {
        $prepay = $this->prepay();
        if ($prepay->error !== 0) {
            return $prepay;
        }
        $order = $prepay->data;
        if ($order["payed"] == 1) {
            return httpMessage("该笔订单已支付，无需重复支付");
        }
        //查看是否已创建有同订单的收据
        $ticket = CashierModel::emptyQuery()
            ->where("orderType", $order["orderType"])
            ->where("orderId", $order["orderId"])
            ->first();
        if (!empty($ticket)) {
            //查看支付金额，如果金额一致，直接返回，如果金额不一致，则重新创建收银单据，防止二次支付，退款处理麻烦
            $amount = bcsub($ticket["amount"], $ticket["payedAmount"], 2);
            if ($amount == $order["amount"]) {
                //直接返回ID即可
                return httpMessage([
                    "id"    => $ticket["id"]
                ]);
            }
        }
        $user = authUserInfo();
        //创建一个收银单据
        $ticket = [
            "id"            => makeOrderId(),
            "uid"           => $user ? $user["uid"] : 0,
            "orderId"       => $order["orderId"],
            "orderType"     => $order["orderType"],
            "amount"        => $order["amount"],
            "currency"      => $order["currency"],
            "currencyRatio" => 1,
            "ip"            => getIP(),
            "payed"         => 0,
            "info"          => $order["info"] ?? null
        ];
        $db = CashierModel::emptyQuery();
        if ($db->insert($ticket)) {
            return httpMessage([
                "id"        => $ticket["id"]
            ]);
        }
        return httpMessage("创建收银单据失败，请稍后再试");
    }

    /**
     * -- doc
     * name: 调用在线支付
     * desc: 调用成功，可获得相应客户端需要使用的参数
     * @return Message
     */
    public function onlinePay(): Message
    {
        //收银单ID
        $id = POST("id", POST("cashierId"));
        $id = is_numeric($id) && $id > 0 ? ceil($id) : 0;
        if ($id <= 0) {
            return httpMessage("缺少收银单据ID");
        }
        //支付方式
        $name = POST("name", POST("payment"));
        $name = !empty($name) ? Str::trim($name) : null;
        if (empty($name)) {
            return httpMessage("未指定支付方式");
        }
        //查看是否是合规的第三方支付
        $payments = CashierModel::onlinePayments();
        if (!in_array($name, array_keys($payments))) {
            return httpMessage("支付方式无效");
        }
        if (!$payments[$name]["enable"]) {
            return httpMessage("该支付方式暂不可用");
        }
        //取数据
        $cashier = CashierModel::getRowByPrimaryKey($id);
        if (empty($cashier)) {
            return httpMessage("收银单据ID无效");
        }
        //判断支付情况
        if ($cashier["payed"] == 1) {
            return httpMessage(0, '已支付的单据', [
                "payed"     => 1
            ]);
        }
        //禁止二次支付 2023.9.23
        if ($cashier["payedAmount"] > 0) {
            return httpMessage("该收银单据已支付，不可二次支付");
        }
        //计算剩余需要支付的金额
        $amount = bcsub($cashier["amount"], $cashier["payedAmount"], 2);
        if ($amount <= 0) {
            //标记为已支付
            @CashierModel::emptyQuery()->where("id", $id)->update([
                "payed"     => 1
            ]);
            return httpMessage(0, '已支付的单据', [
                "payed"     => 1
            ]);
        }
        //仅支持人民币
        if ($cashier["currency"] !== "CNY") {
            return httpMessage("暂不支持人民币以外的结算订单使用在线支付");
        }
        //额外参数，根据不同的支付方式，参数不同，由支付方式决定
        $params = POST("params");
        $params = is_array($params) ? $params : (!empty($params) ? (@json_decode($params, true) ?? []) : []);
        //使用中间键处理，必须有数据返回，否则处理失败
        $result = Middlewares::process(FINANCE_ONLINE_PAY, [
            //支付方式
            "name"      => $name,
            "payment"   => $name,
            //额外参数，供支付方式使用处理
            "params"    => $params,
            //需要支付的金额，必须是正常的货币
            "amount"    => $amount,
            //收银单ID
            "cashierId" => $cashier["id"],
            //结算货币
            "currency"  => $cashier["currency"],
            //订单名称
            "subject"   => CashierModel::orderName($cashier["orderType"]),
            //信息
            "body"      => $cashier["info"] ?? null
        ]);
        $result = is_array($result) ? mergeArray2to1($result) : [];
        if (empty($result)) {
            return httpMessage("支付处理失败，该支付方式不可使用");
        }
        if ($result["error"] !== 0) {
            return httpMessage($result["error"], $result["message"] ?? '支付接口处理失败', $result["data"] ?? null);
        }
        $data = $result["data"] ?? [];
        return httpMessage(0, 'success', [
            "payed"         => 0,
            "redirectUrl"   => $data["redirectUrl"] ?? null,
            "data"          => $data["data"] ?? []
        ]);
    }

    /**
     * 列表管理
     * @param array|null $params
     * @return Message
     */
    #[auth] public function list(?array $params = null): Message
    {
        $params = $params ?? getClient()->get;
        $admin = $params["admin"] ?? 0;
        $admin = $admin == 1 ? 1 : 0;
        $sso = Modules::getModule("sso");
        //判断是不是管理员
        $admin = $admin == 1 && ($this->user["admin"] == 1 || ($sso && !empty($this->user["groups"]) && \app\sso\model\UserModel::matchPermission(["finance/cashier/list"], $this->user["groups"])));
        $db = CashierModel::emptyQuery("c")
            ->select([
                "c.*"
            ]);
        if ($admin && $sso) {
            $db->leftJoin(\app\sso\model\UserModel::tableName()." u", "c.uid=u.uid")
                ->addSelect([
                    "u.username, u.nickname, u.countryCode, u.phone"
                ]);
        }
        //非管理模式，仅获取自己账号的
        if (!$admin) {
            $db->where("c.uid", $this->uid);
        }
        //支持订单类型
        $orderType = $params["orderType"] ?? null;
        $orderType = !empty($orderType) ? Str::trim($orderType) : null;
        if (!empty($orderType)) {
            $db->where("c.orderType", $orderType);
        }
        //是否已支持
        $payed = $params["payed"] ?? '';
        if ($payed == 1 || $payed == '0') {
            $db->where("c.payed", $payed);
        }
        $payments = CashierModel::payments();
        //支付方式
        $payment = $params["payment"] ?? '';
        $payment = !empty($payment) ? trim($payment) : null;
        if (!empty($payment) && in_array($payment, array_keys($payments))) {
            $db->whereFindInSet($payment, "c.payment");
        }
        //是否有退款
        $refund = $params["refund"] ?? '';
        if ($refund == 1 || $refund == '0') {
            $db->where("c.refund", $refund);
        }
        //搜索
        $keyword = $params["keyword"];
        $keyword = !empty($keyword) ? Str::trim($keyword) : null;
        if (!empty($keyword)) {
            if ($sso && $admin) {
                if (is_numeric($keyword) && strlen($keyword) < 12) {
                    //数字，查询手机号码
                    $db->where(function (Query $q) use ($keyword) {
                        $q->whereLike("u.phone", $keyword)
                            ->orWhereLike("c.orderId", $keyword);
                    });
                } else {
                    //其它，查询昵称
                    $db->where(function(Query $q) use($keyword) {
                        $q->whereLike("u.username", $keyword)
                            ->orWhereLike("from_base64(substr(u.nickname, 23))", $keyword);
                    });
                }
            } else {
                $db->where(function (Query $q) use ($keyword) {
                    $q->whereLike("c.id", $keyword)
                        ->orWhereLike("c.orderId", $keyword)
                        ->orWhereLike("c.bak", $keyword);
                });
            }
        }
        //行数
        $rows = $db->rows();
        //当前页数
        $page = $params['page'] ?? 1;
        $page = is_numeric($page) && $page > 0 ? ceil($page) : 1;
        //总页数
        $pages = 1;
        //列表数据
        $list = [];
        if ($rows > 0) {
            $total = $params["total"] ?? 30;
            $total = is_numeric($total) && $total > 2 ? $total : 30;
            $pages = ceil($rows / $total);
            $page = $page > $pages ? $pages : $page;
            $offset = $total * ($page - 1);
            $list = $db->orderBy("c.time", "desc")->get($total, $offset);
            foreach ($list as &$item)
            {
                if ($sso && $admin) {
                    $item['nickname'] = \app\sso\model\UserModel::decodeNickname($item['nickname']);
                }
                $item["orderName"] = CashierModel::orderName($item["orderType"]);
                $item["paymentName"] = CashierModel::getPayment($item["payment"]);
                $item["currencyUnit"] = CashierModel::currencyUnit($item["currency"]);
                //可退款金额
                $maxAmount = bcsub($item["payedAmount"], $item["refundAmount"], 2);
                //查询正在退款的单
                $refundTotal = RefundModel::emptyQuery()
                    ->where("cashierId", $item['id'])
                    ->whereNotIn("state", [0,1])
                    ->sum("amount");
                $refundTotal = !empty($refundTotal) ? $refundTotal : 0;
                $item["refundWaitAmount"] = $refundTotal;
                $maxAmount = bcsub($maxAmount, $refundTotal, 2);
                if (!$admin) {
                    //非管理员，清除部分字段
                    Arr::unsetKey($item, [
                        "uid", "paymentAppId", "payedResult", "editor", "notify", "notified"
                    ]);
                }
                $item["canRefund"] = $maxAmount > 0 ? 1 : 0;
            }
        }
        return httpMessage(compact("rows", "page", "pages", "list"));
    }

    /**
     * 操作退款(仅授权人员可操作)
     * 提交收银单号、退款金额、退款原因
     * @return Message
     */
    #[auth('finance/cashier/refund')] public function refund(?array $params = null): Message
    {
        $params = $params ?? getClient()->post;
        $cashierId = $params["cashierId"] ?? ($params["id"] ?? 0);
        $cashierId = is_numeric($cashierId) && $cashierId > 0 ? $cashierId : 0;
        if ($cashierId <= 0) {
            return httpMessage("收银单据ID无效");
        }
        $amount = $params["amount"] ?? 0;
        $amount = is_numeric($amount) && $amount > 0 ? round($amount, 2) : 0;
        if ($amount <= 0) {
            return httpMessage("退款金额必须大于0");
        }
        $bak = $params["bak"] ?? "";
        $bak = !empty($bak) ? trim($bak) : null;
        if (empty($bak)) {
            return httpMessage("缺少退款原因备注");
        }
        //获取收银单
        $ticket = CashierModel::getRowByPrimaryKey($cashierId);
        if (empty($ticket)) {
            return httpMessage("单号无效，未找到数据");
        }
        //可退款金额
        $maxAmount = bcsub($ticket["payedAmount"], $ticket["refundAmount"], 2);
        //查询正在退款的单
        $refundTotal = RefundModel::emptyQuery()
            ->where("cashierId", $cashierId)
            ->whereNotIn("state", [0,1])
            ->sum("amount");
        $refundTotal = !empty($refundTotal) ? $refundTotal : 0;
        $maxAmount = bcsub($maxAmount, $refundTotal, 2);
        if ($maxAmount < $amount) {
            return httpMessage("该收银单可退款数额为：{$maxAmount}，无法操作！");
        }
        return CashierModel::refundByCashierId($cashierId, $amount, $bak, $this->uid);
    }

    public function paymentList(): array
    {
        $list = CashierModel::payments();
        $result = [];
        foreach ($list as $key => $value) {
            $result[] = [
                "payment"      => $key,
                "name"          => $value
            ];
        }
        return $result;
    }

    public function payments(): array
    {
        return CashierModel::payments();
    }

}